#include <iostream>
#include <sys/select.h>

#include "MyPthread/Log.hpp"
#include "Common.hpp"
#include "InetAddr.hpp"
#include "Socket.hpp"
/*
 #include <sys/select.h>

int select(int nfds, fd_set *readfds, fd_set *writefds,
  fd_set *exceptfds, struct timeval *timeout);
void FD_CLR(int fd, fd_set *set);
int  FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);
*/
namespace SelectServer
{
    using namespace SocketModule;
    using namespace LogModule;
#define NUM sizeof(fd_set) * 8

    const int gdefaultfd = -1;

    class SelectServer
    {
    public:
        SelectServer(uint16_t port)
            : _port(port), _listen_socket(nullptr), _isrunning(false)
        {
            _listen_socket = std::make_unique<TcpSocket>(port);
        }
        void Init()
        {
            for (int i = 0; i < NUM; i++)
            {
                _fd_array[i] = gdefaultfd;
            }

            // 将监听套接字加入到fd_set中
            _listen_socket->BuildTcpSocketMethod(_port);
            _fd_array[0] = _listen_socket->Fd();
        }

        void Loop()
        {
            fd_set readfds;
            _isrunning = true;
            while (_isrunning)
            {
                // 每次清空fds
                FD_ZERO(&readfds);
                struct timeval timeout = {0, 0};
                int maxn = -1;
                // 将关心的fd加入fds
                for (int i = 0; i < NUM; i++)
                {
                    if (_fd_array[i] != gdefaultfd)
                    {
                        FD_SET(_fd_array[i], &readfds);
                    }
                    if (maxn < _fd_array[i])
                        maxn = _fd_array[i];
                }

                // LOG(LogLevel::DEBUG) << "select wait...";
                // 让select来负责进行就绪事件的检测

                int n = ::select(maxn + 1, &readfds
                    , nullptr, nullptr, &timeout);

                switch (n)
                {
                case -1:
                    // 出错了
                    perror("select");
                    break;
                case 0:
                    // 没有就绪事件
                    std::cout << "time out..." << std::endl;
                    break;
                default:
                    // 有就绪事件
                    std::cout << "有事件就绪啦..., timeout: " << timeout.tv_sec << ":" << timeout.tv_usec << std::endl;
                    Dispatcher(&readfds);
                    break;
                }
                sleep(1);
            }
            _isrunning = false;

        }

        void Dispatcher(fd_set *readfds)
        {
            for (int i = 0; i < NUM; i++)
            {
                if (_fd_array[i] == gdefaultfd)
                    continue;
                // 文件描述符，先得是合法的
                if (FD_ISSET(_fd_array[i], readfds))
                {
                    if (_fd_array[i] == _listen_socket->Fd())
                    {
                        // 有新连接
                        Accepter();
                    }
                    else
                    {
                        // 有数据可读
                        Recver(i);
                    }
                }
            }
        }
        void Accepter()
        {
            InetAddr client;
            // listensockfd就绪了！获取新连接不就好了吗？
            int newfd = _listen_socket->Accepter(&client)->Fd();
            if (newfd < 0)
                return;
            else
            {
                std::cout << "获得了一个新的链接: " << newfd << " client info: " << client.Addr() << std::endl;

                // 把newfd也加入辅助数组，让Select关注读事件
                int pos = -1;
                for (int i = 0; i < NUM; i++)
                {
                    if (_fd_array[i] == gdefaultfd)
                    {
                        pos = i;
                        break;
                    }
                }
                if(pos == -1)
                {
                    LOG(LogLevel::ERROR) << "fd_array 已满!";
                    return;
                }
                _fd_array[pos] = newfd;
                LOG(LogLevel::DEBUG) << "新连接加入辅助数组, pos: " << pos<<", sockfd: " << _fd_array[pos];
            }
        }
        void Recver(int who) // 回调函数
        {
            char buffer[1024];
            ssize_t n = recv(_fd_array[who], buffer, sizeof(buffer) - 1, 0); // 不会被阻塞,就绪了
            if (n > 0)
            {
                buffer[n] = 0;
                std::cout << "client# " << buffer << std::endl;
                // 把读到的信息，在回显会去
                std::string message = "echo# ";
                message += buffer;
                send(_fd_array[who], message.c_str(), message.size(), 0); // bug
            }
            else if (n == 0)
            {
                LOG(LogLevel::DEBUG) << "客户端退出, sockfd: " << _fd_array[who];
                close(_fd_array[who]);
                _fd_array[who] = gdefaultfd;
            }
            else
            {
                LOG(LogLevel::DEBUG) << "客户端读取出错, sockfd: " << _fd_array[who]<<"who:"<<who;
                close(_fd_array[who]);
                _fd_array[who] = gdefaultfd;
            }
        }

    private:
        uint16_t _port;
        std::unique_ptr<Socket> _listen_socket;
        bool _isrunning;
        int _fd_array[NUM];
    };

}